package se.perp.c4.control;

import se.perp.c4.control.iface.IMoveValidator;
import java.awt.Point;
import java.util.List;
import se.perp.c4.model.Actor;
import se.perp.c4.model.ActorCollection;
import se.perp.c4.control.iface.IGamePieceListener;
import se.perp.c4.control.iface.IMoveListener;
import se.perp.c4.model.Territory.ETerritory;
import se.perp.c4.model.Actor.EActor;
import se.perp.c4.model.Move;
import se.perp.c4.model.Territory;
import se.perp.c4.model.TerritoryCollection;
import se.perp.c4.ui.ActorViewer;
import se.perp.c4.ui.GamePiece;
import se.perp.c4.ui.MainWindow;
import se.perp.c4.ui.PieceCollection;
import se.perp.c4.ui.PieceOrderer;
import se.perp.c4.ui.RegionBounds;

class PieceHandler implements IGamePieceListener {

    private MainWindow mainWindow;
    private ActorViewer actorViewer;
    private ActorCollection actorColl;
    private TerritoryCollection terrColl;
    private PieceCollection pieceColl;
    private IMoveValidator moveValidator;
    private IMoveListener moveListener;

    public PieceHandler(
            MainWindow mw,
            ActorViewer av,
            ActorCollection ac,
            TerritoryCollection tc,
            PieceCollection pc,
            IMoveValidator mv,
            IMoveListener mcl) {
        mainWindow = mw;
        actorViewer = av;
        actorColl = ac;
        terrColl = tc;
        pieceColl = pc;
        moveValidator = mv;
        moveListener = mcl;
    }

    public void pieceDropped(GamePiece source, Point where) {
        EActor aid = source.getActor();

        Actor a = actorColl.get(aid);

        GamePiece defPiece = pieceColl.getAtThatsNot(where, source);
        Actor def = null;
        if (defPiece != null) {
            def = actorColl.get(defPiece.getActor());
        }

        ETerritory et = RegionBounds.TerritoryIdAt(where);
        Territory to = terrColl.get(et);

        boolean friendlyMove = (def == null || def.side == a.side);

        Territory from = terrColl.get(a.where);

        EActor defId = EActor.NOBODY;
        if (def != null && def.side == a.side) {
            def = null;
        } else if (def != null) {
            defId = def.who;
        }

        if (!moveValidator.isValidMove(a, to, def)) {
            source.snapback();
            return;
        }

        Move move = new Move(aid, defId, from.which, to.which);

        if (friendlyMove) {

            terrColl.moveActor(a, to.which);

            PieceOrderer.orderIn(terrColl, pieceColl, from);
            PieceOrderer.orderIn(terrColl, pieceColl, to);

            source.remember();
            source.setNormalBackground();

            moveListener.moveMade(move);
        } else {

            // Special case: when Frodo moves from either Dagorlad or
            // Gondor, it doesn't matter if an enemy piece is hovered,
            // there will be no attack.
            // We do not perform the actual move, to prevent errors
            // when there are 4 dark characters in MORDOR. The game is
            // over when this happens, anyway, so we don't have to adjust the map.
            if (aid == EActor.FRODO && to.which == ETerritory.MORDOR) {

                assert a.where == ETerritory.GONDOR || a.where == ETerritory.DAGORLAD;

                moveListener.moveMade(move);
            } else {
                moveListener.attackMoveMade(move);
            }
        }
    }

    public void pieceClicked(GamePiece source, boolean doubleClick, int keyModifiers) {
        if (source != null) {
            Actor a = actorColl.get(source.getActor());
            actorViewer.setActor(a);

            if (source.isMovable()) {
                List<ETerritory> terrs = moveValidator.getValidMoves(
                        actorColl.get(source.getActor()));
                mainWindow.setHighlightedRegions(terrs, true);
            }
        }
    }

    public void pieceReleased(GamePiece source) {
        mainWindow.setHighlightedRegions(null, true);
    }

    public void pieceDragged(GamePiece source, Point where) {
        source.getParent().setComponentZOrder(source, 0);


        for (GamePiece gp : pieceColl) {

            if (gp.getBounds().contains(where) &&
                    gp != source) {

                Actor attacker = actorColl.get(source.getActor());
                Actor defender = actorColl.get(gp.getActor());
                if (moveValidator.isValidAttack(attacker, defender)) {
                    gp.mark();
                }

            } else if (gp.isMarked()) {
                gp.unmark();
            }
        }
    }
}
